package collectionDemo;

import com.google.gson.Gson;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author Ajie
 * @date 11/28/24
 * @function
ArrayList TODO  报错，因为 ConcurrentModificationException。这是因为集合在遍历过程中检测到了结构性修改。

synchronized (ConcurrentModificationException_Demo.class){} + ArrayList ok.
CopyOnWriteArrayList：适用于读多写少的场景。
Collections.synchronizedList：对现有的 List 包装成线程安全的集合。但是，，，不是线程安全！！！！已测试。
ConcurrentHashMap：如果集合需要键值对存储。

apples = new LinkedList<>();//报错。。。非线程安全。
apples = Collections.synchronizedList(new ArrayList<>());//只能保证单次操作（如 add、remove）是线程安全的。。。。另一个线程修改了集合，仍会抛出 ConcurrentModificationException。

1- CopyOnWriteArrayList 为什么不报错：

CopyOnWriteArrayList 的设计原理使其能够在读写并发的情况下保证安全性。它通过复制机制避免了并发修改的问题，因此不会抛出 ConcurrentModificationException。具体原因如下：

2- CopyOnWriteArrayList 的工作原理
    1.	读操作：
    •	读取操作（例如 get() 或遍历）是直接基于当前的底层数组进行的。
    •	由于该数组在写操作时不会被修改，因此可以保证遍历操作的一致性。
    2.	写操作（例如 add、remove 等）：
    •	写操作不会直接修改当前的底层数组，而是会 复制一份新的数组，在新数组上执行修改。
    •	写操作完成后，底层数组的引用会指向这个新的数组。
    3.	无锁化读操作：
    •	CopyOnWriteArrayList 通过其写时复制特性，避免了传统锁机制对读操作的影响。读操作无需加锁即可安全进行，因为写操作创建了独立的副本。

3.	无锁化读操作：
•	CopyOnWriteArrayList 通过其写时复制特性，避免了传统锁机制对读操作的影响。读操作无需加锁即可安全进行，因为写操作创建了独立的副本。


特性	        ArrayList	                       CopyOnWriteArrayList
线程安全性	  非线程安全，需外部同步处理	          内部线程安全，支持并发操作
读操作是否加锁	需要外部同步，可能需要加锁	          无需加锁，直接读取
写操作的实现	直接修改底层数据	                    创建新副本，修改副本并替换
遍历期间修改	抛出 ConcurrentModificationException	不会抛异常，因遍历与写互相独立
性能（读操作）	较快，无额外开销	                  略慢，因底层数组的副本操作
性能（写操作）	较快，直接修改	                    较慢，需要复制整个数组

 */
public class ConcurrentModificationException_Demo {
    public static void main(String[] args) {
        Person person = new Person();

        final int[] i = {0};
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        Thread.sleep(1);
                        synchronized (ConcurrentModificationException_Demo.class){
                            person.addApple(i[0]++);
                        }
//                        person.addApple(i[0]++);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        //TODO  报错，因为
        // ConcurrentModificationException。这是因为集合在遍历过程中检测到了结构性修改。
        // Exception in thread "Thread-1" java.util.ConcurrentModificationException
        //	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043)
        //	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:997)
        //	at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
        //	at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61)
        //	at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
        //	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:125)
        //	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:243)
        //	at com.google.gson.Gson.toJson(Gson.java:669)
        //	at com.google.gson.Gson.toJson(Gson.java:648)
        //	at com.google.gson.Gson.toJson(Gson.java:603)
        //	at com.google.gson.Gson.toJson(Gson.java:583)
        //	at collectionDemo.ConcurrentModificationException_Demo$2.run(ConcurrentModificationException_Demo.java:39)
        Gson gson = new Gson();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        Thread.sleep(2);
                        synchronized (ConcurrentModificationException_Demo.class){
                            gson.toJson(person);
                        }
//                        gson.toJson(person);
                        System.out.println("ConcurrentModificationException_Demo:run:"+person.apples.size());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }).start();
    }

    static class Person {
        int num;
        List<Integer> apples;

        public void addApple(int a){
            if (apples == null) {
//                apples = new ArrayList<>();
//                apples = new CopyOnWriteArrayList<>();//OK
//                apples = new LinkedList<>();//Error   报错。。。非线程安全。
//                apples = Collections.synchronizedList(new ArrayList<>());//Error,, 只能保证单次操作（如 add、remove）是线程安全的。。。。另一个线程修改了集合，仍会抛出 ConcurrentModificationException。
                apples = new ArrayList<>();//+Lock OK
            }
            apples.add(a);
        }
    }
}
